package com.flutter.ble_demo;

import android.app.Application;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.widget.TextView;

import androidx.annotation.RequiresApi;
import com.alibaba.fastjson.JSON;
import com.borsam.ble.BorsamBleManager;
import com.borsam.ble.callback.BorsamBleScanCallback;
import com.borsam.device.BorsamDevice;
import com.borsam.device.BorsamDeviceType;
import com.clj.fastble.BleManager;
import com.clj.fastble.callback.BleGattCallback;
import com.clj.fastble.callback.BleIndicateCallback;
import com.clj.fastble.callback.BleMtuChangedCallback;
import com.clj.fastble.callback.BleNotifyCallback;
import com.clj.fastble.callback.BleReadCallback;
import com.clj.fastble.callback.BleRssiCallback;
import com.clj.fastble.callback.BleScanAndConnectCallback;
import com.clj.fastble.callback.BleScanCallback;
import com.clj.fastble.callback.BleWriteCallback;
import com.clj.fastble.data.BleDevice;
import com.clj.fastble.exception.BleException;
import com.clj.fastble.scan.BleScanRuleConfig;
import com.clj.fastble.utils.HexUtil;
import com.flutter.ble_demo.utils.BleActionType;
import com.flutter.ble_demo.utils.Constant;
import com.flutter.ble_demo.utils.JSONUtils;
import com.flutter.ble_demo.utils.ParamsUtil;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import static com.flutter.ble_demo.utils.Constant.FACTORY_CHARACTERISTIC_NOTIFY_UUID;
import static com.flutter.ble_demo.utils.Constant.FACTORY_SERVICE_UUID;
import java.math.BigDecimal;

/**
 * @author xiaowenwu
 * @since 2020-02-23
 * 蓝牙sdk通道插件
 */
public class BlePlugin implements MethodChannel.MethodCallHandler, FlutterPlugin {

    private static final String LISTENER_FUNC_NAME = "onListener";

    private MethodChannel methodChannel;

    private static final String  CHANNEL_SINGLE = "bluetooth_plugin_channel";

    private Context mContext;

    public BlePlugin(Context context){
        mContext = context;
    }

    private static BlePlugin blePlugin;

    @Override
    public void onAttachedToEngine(FlutterPluginBinding flutterPluginBinding) {
        //建立通道
        methodChannel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(),CHANNEL_SINGLE);
        //设置回调
        methodChannel.setMethodCallHandler(this);
    }

    public static  BlePlugin getInstance(Context context){
        if(blePlugin == null){
            synchronized (BlePlugin.class){
                if(blePlugin == null){
                    blePlugin =  new BlePlugin(context);
                }
            }
        }
        return blePlugin;
    }

    @Override
    public void onDetachedFromEngine(FlutterPluginBinding flutterPluginBinding) {

    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    @Override
    public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
        //获取函数名称
        String  method =  methodCall.method;
        switch (method){
            case "init":
                this.init(result);
                break;
            case "globalConfig":
                this.setGlobalConfig(methodCall,result);
                break;
            case "initScanRule":
                this.initScanRule(methodCall,result);
                break;
            case "scan":
                this.scan(result);
                break;
            case "connectByDevice":
                this.connectByDevice(methodCall,result);
                break;
            case "connectByMac":
                this.connectByMac(methodCall,result);
                break;
            case "scanAndConnect":
                this.scanAndConnect(methodCall,result);
                break;
            case "cancelScan":
                this.cancelScan(methodCall,result);
                break;
            case "notify":
                this.registerNotify(methodCall,result);
                break;
            case "stopNotify":
                this.stopNotify(methodCall,result);
                break;
            case "indicate":
                this.indicate(methodCall,result);
                break;
            case "stopIndicate":
                this.stopIndicate(methodCall,result);
                break;
            case "write":
                this.write(methodCall,result);
                break;
            case "read":
                this.read(methodCall,result);
                break;
            case "readRssi":
                this.readRssi(methodCall,result);
                break;
            case "setMtu":
                this.setMtu(methodCall,result);
                break;
            case "requestConnectionPriority":
                this.requestConnectionPriority(methodCall,result);
                break;
            case "disconnect":
                this.disconnect(methodCall,result);
                break;
            case "disconnectAllDevice":
                this.disconnectAllDevice(methodCall,result);
                break;
            case "destroy":
                this.destroy(methodCall,result);
                break;
            case "getAllConnectedDevice":
                this.getAllConnectedDevice(methodCall,result);
                break;
            case "getBluetoothGatt":
                this.getBluetoothGatt(methodCall,result);
                break;
            case "getBluetoothGattServices":
                this.getBluetoothGattServices(methodCall, result);
                break;
            case "getBluetoothGattCharacteristics":
                this.getBluetoothGattCharacteristics(methodCall,result);
                break;
            case "isConnected":
                this.isConnected(methodCall,result);
                break;
            case "getConnectState":
                this.getConnectState(methodCall,result);
                break;
            //*******************博声设备接口******************//
            case "initBleHeartSdk":
                this.initBleHeartSdk(mContext,methodCall,result);
                break;
            case "initBleHeartScanRule":
                this.initBleHeartScanRule(methodCall,result);
                break;
            case "startHeartScan":
                this.startHeartScan(methodCall,result);
                break;
            case "startHeartConnect":
                this.startHeartConnect(methodCall,result);
                break;
            default:
                result.notImplemented();
                break;
        }
    }

    /**
     * 初始化sdk
     * @param result
     */
    private void init( MethodChannel.Result result) {
        BleManager.getInstance().init((Application) mContext.getApplicationContext());
        result.success(null);
    }

    /**
     * 全局配置
     * @param methodCall
     * @param result
     */
    private void setGlobalConfig(MethodCall methodCall, MethodChannel.Result result) {
        boolean isEnableLog = ParamsUtil.getParam(methodCall,"enableLog");
        int retryCount = ParamsUtil.getParam(methodCall,"retryCount");
        long interval = ParamsUtil.getParam(methodCall,"interval");
        int splitWriteNum = ParamsUtil.getParam(methodCall,"splitWriteNum");
        long connectOverTime = ParamsUtil.getParam(methodCall,"connectOverTime");
        int operateTimeout = ParamsUtil.getParam(methodCall,"operateTimeout");
        BleManager bleManager =  BleManager.getInstance();
        //是否开启日志，默认开启
        if(!isEnableLog) {
            bleManager.enableLog(isEnableLog);
        }
        //是否设置重连次数与间隔
        if(retryCount>0&&interval>0) {
            bleManager.setReConnectCount(retryCount, interval);
        }
        //是否设置分包
        if(splitWriteNum>0) {
            bleManager.setSplitWriteNum(splitWriteNum);
        }
        //连接超时
        if(connectOverTime>0) {
            bleManager.setConnectOverTime(connectOverTime);
        }
        //响应超时
        if(operateTimeout>0) {
            bleManager.setOperateTimeout(operateTimeout);
        }
        result.success(null);
    }

    /**
     * 初始化扫描规则
     * @param methodCall
     * @param result
     *  Tips：
     * 在扫描设备之前，可以配置扫描规则，筛选出与程序匹配的设备
     *不配置的话均为默认参数
     * 在2.1.2版本及之前，必须先配置过滤规则再扫描；在2.1.3版本之后可以无需配置，开启默认过滤规则的扫描。
     */
    private void initScanRule(MethodCall methodCall, MethodChannel.Result result) {
        UUID[] serviceUuids = ParamsUtil.getParam(methodCall,"serviceUuids");
        String names = ParamsUtil.getParam(methodCall,"names");
        String mac = ParamsUtil.getParam(methodCall,"mac");
        boolean isAutoConnect = ParamsUtil.getParam(methodCall,"isAutoConnect");
        int scanTimeOut = ParamsUtil.getParam(methodCall,"scanTimeOut");
        BleScanRuleConfig.Builder scanRuleConfig = new BleScanRuleConfig.Builder();
        // 只扫描指定的服务的设备，可选
        if(serviceUuids!=null) {
            scanRuleConfig.setServiceUuids(serviceUuids);
        }
        // 只扫描指定广播名的设备，可选
        if(names!=null) {
            scanRuleConfig.setDeviceName(true, names);
        }
        // 只扫描指定mac的设备，可选
        if(mac!=null) {
            scanRuleConfig.setDeviceMac(mac);
        }
        // 连接时的autoConnect参数，可选，默认false
        if(isAutoConnect) {
            scanRuleConfig.setAutoConnect(isAutoConnect);
        }
        // 扫描超时时间，可选，默认10秒；小于等于0表示不限制扫描时间
        if(scanTimeOut!=0) {
            scanRuleConfig.setScanTimeOut(scanTimeOut);
        }
        BleManager.getInstance().initScanRule(scanRuleConfig.build());
        result.success(null);
    }

    /**
     * 扫描
     * @param result
     */
    private void scan(MethodChannel.Result result) {
        BleManager.getInstance().scan(new BleScanCallback() {
            @Override
            public void onScanStarted(boolean success) {
                // 开始扫描（主线程）
                invokeListener(BleActionType.SCAN_START,null);
            }

            @Override
            public void onScanning(BleDevice bleDevice) {
                // 扫描到一个符合扫描规则的BLE设备（主线程）
                Map<String,Object> params = new HashMap<>();
                params.put("bleDevice",bleDevice);
                invokeListener(BleActionType.PROCESSING, params);
            }

            @Override
            public void onLeScan(BleDevice bleDevice) {
                //扫描过程中所有被扫描到的结果回调。由于扫描及过滤的过程是在工作线程中的，此方法也处于工作线程中。
                //同一个设备会在不同的时间，携带自身不同的状态（比如信号强度等），出现在这个回调方法中，出现次数取决于周围的设备量及外围设备的广播间隔。
                //体脂秤的广播数据会出现在这里
                //System.out.println("000000000000===Android onLeScan收到通知==========");
                Map<String,Object> params = new HashMap<>();
                byte[] devixeBytes = bleDevice.getScanRecord();
                String deviceStrings = HexUtil.formatHexString(devixeBytes).toLowerCase();
                if(deviceStrings.indexOf("ffff3003030733") >= 0){
                    System.out.println("体脂秤----onLeScan收到广播通知："+deviceStrings);
                    params.put("weight", convertWeight(HexUtil.formatHexString(devixeBytes)));//体重
                    params.put("impedance", convertImpedance(HexUtil.formatHexString(devixeBytes)));//阻抗
                    params.put("isLock", HexUtil.formatHexString(devixeBytes).substring(36,38));//是否最终值 AA或者AB即是，需要上传数据
                    params.put("mac", bleDevice.getMac());//设备mac地址
                    params.put("oringin", HexUtil.formatHexString(devixeBytes).toLowerCase());//原始消息数据
                    invokeListener(BleActionType.ONLESCAN, params);
                }
            }

            @Override
            public void onScanFinished(List<BleDevice> scanResultList) {
                // 扫描结束，列出所有扫描到的符合扫描规则的BLE设备（主线程）
                Map<String,Object> params = new HashMap<>();
                params.put("scanResultList",scanResultList.size()>0?scanResultList:null);
                invokeListener(BleActionType.SCAN_SUCCESS,params);
            }
        });
        result.success(null);
    }

    /**
     * 通过扫描到的BleDevice对象进行连接。
     * @param methodCall
     * @param result
     */
    private void connectByDevice(MethodCall methodCall, MethodChannel.Result result) {
        //转换参数
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");

        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        if(bleDevice!=null){
            BleManager.getInstance().connect(bleDevice, new BleGattCallback() {
                @Override
                public void onStartConnect() {
                    // 开始连接
                    invokeListener(BleActionType.CONNECT_START,null);
                }

                @Override
                public void onConnectFail(BleDevice bleDevice, BleException exception) {
                    // 连接失败
                    Map<String,Object> params = new HashMap<>();
                    params.put("bleDevice",bleDevice);
                    params.put("exception",exception);
                    invokeListener(BleActionType.FAIL,params);
                }

                @Override
                public void onConnectSuccess(BleDevice bleDevice, BluetoothGatt gatt, int status) {
                    // 连接成功，BleDevice即为所连接的BLE设备
                    Map<String,Object> params = new HashMap<>();
                    params.put("bleDevice",bleDevice);
                    params.put("gatt",gatt);
                    params.put("status",status);
                    invokeListener(BleActionType.CONNECT_SUCCESS,params);
                }

                @Override
                public void onDisConnected(boolean isActiveDisConnected, BleDevice bleDevice, BluetoothGatt gatt, int status) {
                    // 连接中断，isActiveDisConnected表示是否是主动调用了断开连接方法
                    Map<String,Object> params = new HashMap<>();
                    params.put("isActiveDisConnected",isActiveDisConnected);
                    params.put("bleDevice",bleDevice);
                    params.put("gatt",gatt);
                    params.put("status",status);
                    invokeListener(BleActionType.DISCONNECT,params);
                }
            });
            result.success(null);
        }else{
            result.error("Missing parameter", "Cannot find parameter `" + bleDevice + "` or `" + bleDevice + "` is null!", 5);
            throw new RuntimeException("Cannot find parameter `" + bleDevice + "` or `" + bleDevice + "` is null!");
        }
    }

    /**
     * Tips:
     * 此方法可以不经过扫描，尝试直接连接周围复合该Mac的BLE设备。
     * 在很多使用场景，我建议APP保存用户惯用设备的Mac，然后使用该方法进行连接可以大大提高连接效率。
     * @param methodCall
     * @param result
     */
    private void connectByMac(MethodCall methodCall, MethodChannel.Result result) {
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");
        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        BleManager.getInstance().connect(bleDevice, new BleGattCallback() {
            @Override
            public void onStartConnect() {
                // 开始连接
                invokeListener(BleActionType.CONNECT_START,null);
            }

            @Override
            public void onConnectFail(BleDevice bleDevice, BleException exception) {
                // 连接失败
                Map<String,Object> params = new HashMap<>();
                params.put("bleDevice",bleDevice);
                params.put("exception",exception.getDescription());
                invokeListener(BleActionType.CONNECT_FAIL,params);
            }

            @Override
            public void onConnectSuccess(BleDevice bleDevice, BluetoothGatt gatt, int status) {
                // 连接成功，BleDevice即为所连接的BLE设备
                Map<String,Object> params = new HashMap<>();
                params.put("bleDevice",bleDevice);
                params.put("uuid_service",FACTORY_SERVICE_UUID);
                params.put("uuid_characteristic_notify",FACTORY_CHARACTERISTIC_NOTIFY_UUID);
                params.put("status",status);
                invokeListener(BleActionType.CONNECT_SUCCESS,params);
            }

            @Override
            public void onDisConnected(boolean isActiveDisConnected, BleDevice bleDevice, BluetoothGatt gatt, int status) {
                // 连接中断，isActiveDisConnected表示是否是主动调用了断开连接方法
                Map<String,Object> params = new HashMap<>();
                params.put("isActiveDisConnected",isActiveDisConnected);
                params.put("bleDevice",bleDevice);
                params.put("status",status);
                invokeListener(BleActionType.DISCONNECT,params);
            }
        });
        result.success(null);
    }

    /**
     * 扫描到首个符合扫描规则的设备后，便停止扫描，然后连接该设备。
     * Tips:
     * - 扫描及过滤过程是在工作线程中进行，所以不会影响主线程的UI操作，但每一个回调结果都会回到主线程。 连接操作会在主线中进行。
     * @param methodCall
     * @param result
     */
    private void scanAndConnect(MethodCall methodCall, MethodChannel.Result result) {
        BleManager.getInstance().scanAndConnect(new BleScanAndConnectCallback() {
            @Override
            public void onScanStarted(boolean success) {
                // 开始扫描（主线程）
                invokeListener(BleActionType.SCAN_START,null);
            }

            @Override
            public void onScanning(BleDevice bleDevice) {
                Map<String,Object> params = new HashMap<>();
                params.put("bleDevice",bleDevice);
                invokeListener(BleActionType.PROCESSING,params);
            }

            @Override
            public void onScanFinished(BleDevice scanResult) {
                // 扫描结束，结果即为扫描到的第一个符合扫描规则的BLE设备，如果为空表示未搜索到（主线程）
                Map<String,Object> params = new HashMap<>();
                params.put("bleDevice",scanResult);
                invokeListener(BleActionType.SCAN_SUCCESS,params);
            }

            @Override
            public void onStartConnect() {
                // 开始连接（主线程）
                invokeListener(BleActionType.CONNECT_START,null);
            }

            @Override
            public void onConnectFail(BleDevice bleDevice,BleException exception) {
                // 连接失败（主线程）
                Map<String,Object> params = new HashMap<>();
                params.put("bleDevice",bleDevice);
                params.put("exception",exception.getDescription());
                invokeListener(BleActionType.FAIL,params);
            }

            @Override
            public void onConnectSuccess(BleDevice bleDevice, BluetoothGatt gatt, int status) {
                // 连接成功，BleDevice即为所连接的BLE设备（主线程）
                Map<String,Object> params = new HashMap<>();
                params.put("bleDevice",bleDevice);
                params.put("gatt",gatt);
                params.put("status",status);
                invokeListener(BleActionType.CONNECT_SUCCESS,params);
            }

            @Override
            public void onDisConnected(boolean isActiveDisConnected, BleDevice device, BluetoothGatt gatt, int status) {
                // 连接断开，isActiveDisConnected是主动断开还是被动断开（主线程）
                Map<String,Object> params = new HashMap<>();
                params.put("isActiveDisConnected",isActiveDisConnected);
                params.put("bleDevice",device);
                params.put("gatt",gatt);
                params.put("status",status);
                invokeListener(BleActionType.DISCONNECT,params);
            }
        });
        result.success(null);
    }

    /**
     * Tips:
     * - 调用该方法后，如果当前还处在扫描状态，会立即结束，并回调`onScanFinished`方法
     * @param methodCall
     * @param result
     */
    private void cancelScan(MethodCall methodCall, MethodChannel.Result result) {
        BleManager.getInstance().cancelScan();
        result.success(null);
    }

    /**
     * 订阅通知notify
     * @param methodCall
     * @param result
     */
    private void registerNotify(MethodCall methodCall, MethodChannel.Result result) {
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");
        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        String uuid_service = ParamsUtil.getParam(methodCall,result,"uuid_service");
        String uuid_characteristic_notify = ParamsUtil.getParam(methodCall,"uuid_characteristic_notify");
        BleManager.getInstance().notify(
                bleDevice,
                uuid_service,
                uuid_characteristic_notify,
                new BleNotifyCallback() {
                    @Override
                    public void onNotifySuccess() {
                        // 打开通知操作成功
                        invokeListener(BleActionType.SUCCESS,null);
                    }

                    @Override
                    public void onNotifyFailure(BleException exception) {
                        // 打开通知操作失败
                        Map<String,Object> params = new HashMap<>();
                        params.put("exception",exception.getDescription());
                        invokeListener(BleActionType.FAIL,params);
                    }

                    @Override
                    public void onCharacteristicChanged(byte[] data) {
                        // 打开通知后，设备发过来的数据将在这里出现
                        Map<String,Object> params = new HashMap<>();
                        params.put("data", convertTemperature(HexUtil.encodeHexStr(data)));
                        params.put("oringin",HexUtil.encodeHexStr(data));
                        params.put("bleDevice",bleDevice);
                        invokeListener(BleActionType.READ_SUCCESS,params);
                    }
                });
    }

    /**
     * 取消订阅通知notify，并移除数据接收的回调监听
     * @param methodCall
     * @param result
     */
    private void stopNotify(MethodCall methodCall, MethodChannel.Result result) {
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");
        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        String uuid_service = ParamsUtil.getParam(methodCall,result,"uuid_service");
        String uuid_characteristic_notify = ParamsUtil.getParam(methodCall,result,"uuid_characteristic_notify");
        BleManager.getInstance().stopNotify(bleDevice,uuid_service, uuid_characteristic_notify);
        result.success(null);
    }

    /**
     *订阅通知indicate
     * @param methodCall
     * @param result
     */
    private void indicate(MethodCall methodCall, MethodChannel.Result result) {
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");
        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        String uuid_service = ParamsUtil.getParam(methodCall,result,"uuid_service");
        String uuid_characteristic_indicate = ParamsUtil.getParam(methodCall,result,"uuid_characteristic_indicate");
        BleManager.getInstance().indicate(
                bleDevice,
                uuid_service,
                uuid_characteristic_indicate,
                new BleIndicateCallback() {
                    @Override
                    public void onIndicateSuccess() {
                        // 打开通知操作成功
                        invokeListener(BleActionType.SUCCESS,null);
                    }

                    @Override
                    public void onIndicateFailure(BleException exception) {
                        // 打开通知操作失败
                        Map<String,Object> params = new HashMap<>();
                        params.put("exception",exception.getDescription());
                        invokeListener(BleActionType.FAIL,params);
                    }

                    @Override
                    public void onCharacteristicChanged(byte[] data) {
                        // 打开通知后，设备发过来的数据将在这里出现
                        Map<String,Object> params = new HashMap<>();
                        params.put("data",data);
                        invokeListener(BleActionType.PROCESSING,params);
                    }
                });
    }

    /**
     * 取消订阅通知indicate，并移除数据接收的回调监听
     * @param methodCall
     * @param result
     */
    private void stopIndicate(MethodCall methodCall, MethodChannel.Result result) {
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");
        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        String uuid_service = ParamsUtil.getParam(methodCall,result,"uuid_service");
        String uuid_characteristic_indicate = ParamsUtil.getParam(methodCall,result,"uuid_characteristic_indicate");
        BleManager.getInstance().stopIndicate(bleDevice,uuid_service, uuid_characteristic_indicate);
        result.success(null);
    }

    /**
     * 写操作
     *Tips:
     * - 在没有扩大MTU及扩大MTU无效的情况下，当遇到超过20字节的长数据需要发送的时候，需要进行分包。参数`boolean split`表示是否使用分包发送；无`boolean split`参数的`write`方法默认对超过20字节的数据进行分包发送。
     * - 关于`onWriteSuccess`回调方法: `current`表示当前发送第几包数据，`total`表示本次总共多少包数据，`justWrite`表示刚刚发送成功的数据包。
     * - 对于分包发送的辅助策略，可以选择发送上一包数据成功之后发送下一包，或直接发送下一包，参数`sendNextWhenLastSuccess`表示是否待收到`onWriteSuccess`之后再进行下一包的发送。默认true。
     * - 参数`intervalBetweenTwoPackage`表示延时多长时间发送下一包，单位ms，默认0。
     * @param methodCall
     * @param result
     */
    private void write(MethodCall methodCall, MethodChannel.Result result) {
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");
        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        String uuid_service = ParamsUtil.getParam(methodCall,result,"uuid_service");
        String uuid_characteristic_write = ParamsUtil.getParam(methodCall,result,"uuid_characteristic_write");
        byte[] data =  ParamsUtil.getParam(methodCall,result,"data");
        BleManager.getInstance().write(
                bleDevice,
                uuid_service,
                uuid_characteristic_write,
                data,
                new BleWriteCallback() {
                    @Override
                    public void onWriteSuccess(int current, int total, byte[] justWrite) {
                        // 发送数据到设备成功
                        invokeListener(BleActionType.SUCCESS,null);
                    }

                    @Override
                    public void onWriteFailure(BleException exception) {
                        // 发送数据到设备失败
                        invokeListener(BleActionType.FAIL,null);
                    }
                });
        result.success(null);
    }

    /**
     * 读操作
     * @param methodCall
     * @param result
     */
    private void read(MethodCall methodCall, MethodChannel.Result result) {
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");
        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        String uuid_service = ParamsUtil.getParam(methodCall,result,"uuid_service");
        String uuid_characteristic_read = ParamsUtil.getParam(methodCall,result,"uuid_characteristic_read");
        BleManager.getInstance().read(
                bleDevice,
                uuid_service,
                uuid_characteristic_read,
                new BleReadCallback() {
                    @Override
                    public void onReadSuccess(byte[] data) {
                        // 读特征值数据成功
                        Map<String,Object> params = new HashMap<>();
                        params.put("data",data);
                        invokeListener(BleActionType.SUCCESS,params);
                    }

                    @Override
                    public void onReadFailure(BleException exception) {
                        // 读特征值数据失败
                        Map<String,Object> params = new HashMap<>();
                        params.put("exception",exception.getDescription());
                        invokeListener(BleActionType.FAIL,params);
                    }
                });
    }

    /**
     * 获取设备的信号强度Rssi
     * 获取设备的信号强度，需要在设备连接之后进行。
     * 某些设备可能无法读取Rssi，不会回调onRssiSuccess(),而会因为超时而回调onRssiFailure()。
     * @param methodCall
     * @param result
     */
    private void readRssi(MethodCall methodCall, MethodChannel.Result result) {
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");
        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        BleManager.getInstance().readRssi(
                bleDevice,
                new BleRssiCallback() {

                    @Override
                    public void onRssiFailure(BleException exception) {
                        // 读取设备的信号强度失败
                        Map<String,Object> params = new HashMap<>();
                        params.put("exception",exception.getDescription());
                        invokeListener(BleActionType.FAIL,params);
                    }

                    @Override
                    public void onRssiSuccess(int rssi) {
                        // 读取设备的信号强度成功
                        Map<String,Object> params = new HashMap<>();
                        params.put("rssi",rssi);
                        invokeListener(BleActionType.SUCCESS,params);
                    }
                });
        result.success(null);
    }

    /**
     * 设置MTU，需要在设备连接之后进行操作。
     * 默认每一个BLE设备都必须支持的MTU为23。
     * MTU为23，表示最多可以发送20个字节的数据。
     * 在Android 低版本(API-17 到 API-20)上，没有这个限制。所以只有在API21以上的设备，才会有拓展MTU这个需求。
     * 该方法的参数mtu，最小设置为23，最大设置为512。
     * 并不是每台设备都支持拓展MTU，需要通讯双方都支持才行，也就是说，需要设备硬件也支持拓展MTU该方法才会起效果。调用该方法后，可以通过onMtuChanged(int mtu)查看最终设置完后，设备的最大传输单元被拓展到多少。如果设备不支持，可能无论设置多少，最终的mtu还是23。
     * @param methodCall
     * @param result
     */
    private void setMtu(MethodCall methodCall, MethodChannel.Result result) {
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");
        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        int  mtu = ParamsUtil.getParam(methodCall,result,"mtu");
        BleManager.getInstance().setMtu(bleDevice, mtu, new BleMtuChangedCallback() {
            @Override
            public void onSetMTUFailure(BleException exception) {
                // 设置MTU失败
                Map<String,Object> params = new HashMap<>();
                params.put("exception",exception.getDescription());
                invokeListener(BleActionType.FAIL,params);
            }

            @Override
            public void onMtuChanged(int mtu) {
                // 设置MTU成功，并获得当前设备传输支持的MTU值
                Map<String,Object> params = new HashMap<>();
                params.put("mtu",mtu);
                invokeListener(BleActionType.SUCCESS,params);
            }
        });
        result.success(null);
    }

    /**
     * 设置连接的优先级，一般用于高速传输大量数据的时候可以进行设置。 Must be one of{@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED},
     *  {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH} or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
     * @param methodCall
     * @param result
     */
    private void requestConnectionPriority(MethodCall methodCall, MethodChannel.Result result) {
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");
        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        int  connectionPriority = ParamsUtil.getParam(methodCall,result,"connectionPriority");
        BleManager.getInstance().requestConnectionPriority(bleDevice,connectionPriority);
        result.success(null);
    }

    /**
     * 断开某个设备
     * @param methodCall
     * @param result
     */
    private void disconnect(MethodCall methodCall, MethodChannel.Result result) {
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");
        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        BleManager.getInstance().disconnect(bleDevice);
        result.success(null);
    }

    /**
     * 断开所有设备
     * @param methodCall
     * @param result
     */
    private void disconnectAllDevice(MethodCall methodCall, MethodChannel.Result result) {
        BleManager.getInstance().disconnectAllDevice();
        result.success(null);
    }

    /**
     * 退出使用，清理资源
     * @param methodCall
     * @param result
     */
    private void destroy(MethodCall methodCall, MethodChannel.Result result) {
        BleManager.getInstance().destroy();
        result.success(null);
    }

    /**
     * 获取所有已连接设备
     * @param methodCall
     * @param result
     */
    private void getAllConnectedDevice(MethodCall methodCall, MethodChannel.Result result) {
        List<BleDevice> devices =  BleManager.getInstance().getAllConnectedDevice();
        result.success(devices);
    }

    /**
     * 获取某个已连接设备的BluetoothGatt
     * @param methodCall
     * @param result
     */
    private void getBluetoothGatt(MethodCall methodCall, MethodChannel.Result result) {
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");
        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        BluetoothGatt bluetoothGatt = BleManager.getInstance().getBluetoothGatt(bleDevice);
        result.success(bluetoothGatt);
    }

    /**
     * 获取某个已连接设备的所有Service
     * @param methodCall
     * @param result
     */
    private void getBluetoothGattServices(MethodCall methodCall, MethodChannel.Result result) {
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");
        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        List<BluetoothGattService> bluetoothGattServices = BleManager.getInstance().getBluetoothGattServices(bleDevice);
        result.success(bluetoothGattServices);
    }

    /**
     * 获取某个Service的所有Characteristic
     * @param methodCall
     * @param result
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    private void getBluetoothGattCharacteristics(MethodCall methodCall, MethodChannel.Result result) {
        BluetoothGattService   bluetoothGattService  = JSON.parseObject(ParamsUtil.getParam(methodCall,result,"bluetoothGattService"),BluetoothGattService.class);
        List<BluetoothGattCharacteristic> bluetoothGattServices = BleManager.getInstance().getBluetoothGattCharacteristics(bluetoothGattService);
        result.success(bluetoothGattServices);
    }

    /**
     * 判断某个设备是否已连接
     * @param methodCall
     * @param result
     */
    private void isConnected(MethodCall methodCall, MethodChannel.Result result) {
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");
        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        boolean isConnected = BleManager.getInstance().isConnected(bleDevice);
        result.success(isConnected);
    }

    /**
     * 判断某个设备的当前连接状态
     * @param methodCall
     * @param result
     */
    private void getConnectState(MethodCall methodCall, MethodChannel.Result result) {
        Map bleDeviceMap = ParamsUtil.getParam(methodCall,result,"bleDevice");
        //用反射获取对象
        BluetoothDevice bluetoothDevice = getClassByReflex(bleDeviceMap);
        BleDevice  bleDevice =  BleManager.getInstance().convertBleDevice(bluetoothDevice);
        int state = BleManager.getInstance().getConnectState(bleDevice);
        result.success(state);
    }

    /**
     * 调用监听器
     * @param type   类型
     * @param params 参数
     */
    private void invokeListener(BleActionType type, Object params) {
        Map<String, Object> resultParams = new HashMap<>(2, 1);
        resultParams.put("type", type);
        if(params != null){
            // 字符串单独解析，否则会有双引号
            if(params instanceof String){
                resultParams.put("params", params);
            }else{
                resultParams.put("params", JSONUtils.toJSONString(params));
            }
        }
        methodChannel.invokeMethod(LISTENER_FUNC_NAME, JSONUtils.toJSONString(resultParams));
    }

    /**
     * 解析温度值
     * @param byteStr
     * @return
     */
    private String convertTemperature(String byteStr){
        String strHex = "00" + byteStr.substring(20,22) + byteStr.substring(18,20);
        BigInteger temperature = new BigInteger(strHex,16);
        return new BigDecimal(temperature.toString()).multiply(new BigDecimal("0.01")).setScale(1, BigDecimal.ROUND_DOWN).toString();
    }

    /**
     * 解析体重值
     * @param devixeStrings
     * @return
     */
    private String convertWeight(String devixeStrings){
        String weight = "0.0";
        String accuracy = devixeStrings.substring(38,40);//小数位标识 01一位需要处10 02两位需要除100
        BigInteger weightData = new BigInteger(devixeStrings.substring(40,48),16);
        if(accuracy.equals("01")){
            weight = new BigDecimal(weightData.toString()).multiply(new BigDecimal("0.1")).setScale(1, BigDecimal.ROUND_DOWN).toString();
        }else if(accuracy.equals("02")){
            weight = new BigDecimal(weightData.toString()).multiply(new BigDecimal("0.01")).setScale(1, BigDecimal.ROUND_DOWN).toString();
        }
        return weight;
    }

    /**
     * 解析阻抗值
     * @param devixeStrings
     * @return
     */
    private String convertImpedance(String devixeStrings){
        return new BigInteger(devixeStrings.substring(48,52),16).toString();//十进制整数
    }

    /**
     * 利用反射获取对象
     * @param bleDeviceMap
     * @return
     */
    private BluetoothDevice getClassByReflex(Map bleDeviceMap){
        Class clazz = BluetoothDevice.class;
        Constructor c1= null;
        try {
            c1 = clazz.getDeclaredConstructor(new Class[]{String.class});
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        assert c1 != null;
        c1.setAccessible(true);
        BluetoothDevice bluetoothDevice= null;
        try {
            bluetoothDevice = (BluetoothDevice)c1.newInstance(new Object[]{bleDeviceMap.get("mac")});
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return bluetoothDevice;
    }

    //*****************************************************************************************************//
    //                                          以下是博声心电图相关接口                                        //
    //*****************************************************************************************************//

    /**
     * 初始化博声sdk
     */
    private void initBleHeartSdk(Context context,MethodCall methodCall, MethodChannel.Result result){
        BorsamBleManager.getInstance().init((Application) context.getApplicationContext());
        result.success(null);
    }

    /**
     * 设置扫描规则
     * @param methodCall
     * @param result
     */
    private void  initBleHeartScanRule(MethodCall methodCall, MethodChannel.Result result){
        String deviceName= ParamsUtil.getParam(methodCall,"deviceName");
        String[] deviceNames = new String[5];
        if(deviceName!=null&&deviceName.length()>0){
            deviceNames = deviceName.split(",");
        }else{
            deviceNames[0] = BorsamDeviceType.WECARDIO_STD;
            deviceNames[1] = BorsamDeviceType.WECARDIO_LTS;
        }
        int scanTimeOut = ParamsUtil.getParam(methodCall,"scanTimeOut");
        if(scanTimeOut<=0){
            //默认扫描超时10s
            scanTimeOut = 10*1000;
        }
        com.borsam.blecore.scan.BleScanRuleConfig bleScanRuleConfig = new com.borsam.blecore.scan.BleScanRuleConfig.Builder()
                .setScanTimeOut(scanTimeOut)
                .setDeviceName(true, deviceNames)
                .build();
        BorsamBleManager.getInstance() .initScanRule(bleScanRuleConfig);
        result.success(null);
    }

    /**
     * 开始扫描设备
     * @param methodCall
     * @param result
     * onScanStarted(boolean success);开始扫描回调，在主线程，success表示是否成功启动蓝牙扫描
     * onScanning(BorsamDevice borsamDevice);扫描到符合扫描规则的博声设备时回调，在主线程
     * onScanFinished(List list);扫描结束时会将所有扫描到的博声设备返回
     */
    private  void  startHeartScan(MethodCall methodCall, MethodChannel.Result result){
        String mac = ParamsUtil.getParam(methodCall,"mac");
        BorsamBleManager.getInstance().scan(new BorsamBleScanCallback() {
            @Override public void onScanStarted(boolean success) {
                Map<String,Object> params = new HashMap<>();
                params.put("isStartScanSuccess",success);
                invokeListener(BleActionType.ONHEARTSCANSTARTED,params);
            }
            @Override public void onScanning(BorsamDevice borsamDevice) {
                Map<String,Object> params = new HashMap<>();
                params.put("borsamDeviceName",borsamDevice.getDeviceName());
                if(!TextUtils.isEmpty(mac)&&borsamDevice.getAddress().equals(mac)){
                    EcgViewActivity.start(mContext,borsamDevice);
                    BorsamBleManager.getInstance().cancelScan();
                    invokeListener(BleActionType.ONHEARTSCANFINISHED,params);
                }else{
                    invokeListener(BleActionType.ONHEARTSCANNING,params);
                }

            }
            @Override public void onScanFinished(List<BorsamDevice> list) {
                //扫描完成直接跳转
                if(list!=null&&list.size()>0){
                    Timer timer =   new Timer();
                    timer.schedule(new TimerTask() {
                        @Override
                        public void run() {
                            timer.cancel();
                            EcgViewActivity.start(mContext,list.get(0));
                        }
                    },1000);
                }
                //flutter回调
                Map<String,Object> params = new HashMap<>();
                params.put("deviceList",list);
                invokeListener(BleActionType.ONHEARTSCANFINISHED,params);
            }
        });

        result.success(null);
    }

    //开始连接设备
    private void startHeartConnect(MethodCall methodCall, MethodChannel.Result result){
        String deviceName= ParamsUtil.getParam(methodCall,"deviceName");
        if(!TextUtils.isEmpty(deviceName)){
            //跳转到检测页面
//            EcgViewActivity.start(mContext,BorsamDevice.create(deviceName));
            result.success(null);
        }else{
            result.error("500","设备名称必须设置","设备连接需要指定设备名称");
        }

    }

    //返回测量数据
    public void displayEcgData(String deviceCode,String ecgValue,String recordData){
        Map<String,Object> params = new HashMap<>();
        params.put("deviceCode",deviceCode);
        params.put("ecgValue",ecgValue);
        params.put("recordData",recordData);
        invokeListener(BleActionType.ONHEARTECGDATADISPLAY,params);
    }
}
